/*
 * miniFreeRTOS SOC System emulation.
 *
 * Copyright (c) 2024- yanl1229@163.com.
 * Written by yanl1229
 *
 * This code is licensed under the GPL.
 */
#include "semaphore.h"
#include "mm.h"


static void semaphore_init(struct semaphore * sem)
{
    sem->count = 1;
    list_init(&sem->waiter_list);
}

/**
 * semaphore_create: 创建信号量
*/
struct semaphore * semaphore_create()
{
    struct semaphore *sem = NULL;

    sem = (struct semaphore *)kmalloc(sizeof(struct semaphore));
    if (sem != NULL)
    {
        semaphore_init(sem);
    }


    return sem;
}

void semaphore_delete(struct semaphore *sem)
{
    if (sem != NULL)
        kfree(sem);
}

/**
 *  释放信号量
 */
static uint32_t semaphore_up_common(struct semaphore * sem, int irq)
{
    uint32_t flags;
    for( ; ; )
    {
        if (irq)
            flags = irq_save();
        else
            disable_interrupt();

        /**
         * 释放信号量
         */

        sem->count = sem->count + 1;

        /*
         * 有任务阻塞在等待信号量
         */
        if (!list_is_empty(sem->waiter_list))
        {
            if (remove_from_event_queue(sem->waiter_list))
            {
                /* 有高优先级任务存在，直接抢占当前任务 */
                yield();
            }
        }


        if (irq)
            irq_restore(flags);
        else
            enable_interrupt();

        return 1;

    }
}

/**
 * 获取信号量
 * @sem:         消息队列
 * @xTicksToWait:   超时时间
 */
static uint32_t semaphore_down_common(struct semaphore * sem, int irq)
{
    uint32_t flags;

    for( ; ; )
    {
        if (irq)
            flags = irq_save();
        else
            disable_interrupt();
        {
            uint32_t sem_count = sem->count;

            /**
             * 信号量空闲
             */
            if (sem_count > 0)
            {
                /*
                 * 获取信号量
                 */
                sem->count = sem_count - 1;
                if (irq)
                    irq_restore(flags);
                else
                    enable_interrupt();
                return 1;
            }
        }

        if (irq)
            irq_restore(flags);
        else
            enable_interrupt();

        sched_suspend();

        /*
         * 并且信号还没被释放
         */
        if (sem->count == 0)
        {
            /* 当前任务被挂到等待队列 */
            dequeue_task(current);
            list_add_tail(sem->waiter_list, &current->event_node);

            if (sched_resume() == 0)
            {
                /* 任务已经被阻塞，不可抢占情况下，直接触发任务调度,让出当前cpu */
                yield();
            }
        }
        else
        {
            /* 信号量被释放，再次尝试获取 */
            sched_resume();
        }
    }
}

/**
 * 释放信号量
 */
uint32_t semaphore_up(struct semaphore * sem)
{
    return semaphore_up_common(sem, 0);
}
/**
 * 获取信号量
 */
uint32_t semaphore_down(struct semaphore * sem)
{
    return semaphore_down_common(sem, 0);
}

/**
 * 释放信号量
 */
uint32_t semaphore_up_irq(struct semaphore * sem)
{
    return semaphore_up_common(sem, 1);
}
/**
 * 获取信号量
 */
uint32_t semaphore_down_irq(struct semaphore * sem)
{
    return semaphore_down_common(sem, 1);
}